package jupiter.channel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import jupiter.message.*;
import org.json.*;
import org.mortbay.util.ajax.Continuation;

import jupiter.filters.*;
/**
 * Maintains a list of all other channels for a particular domain
 * @author Justin
 *
 */
public class Domain {
	
	public HashMap<String, ChannelInterface> channelMap = new HashMap<String, ChannelInterface>(); //should be copy on write, b/c this will never change
	
	public HashMap<String, List> channelListMap = new HashMap<String, List>();
	public Address address;
	
	public Domain(Address a) {
		address = a;
		//from address.domain() look up and set rules
		
	}
	

	/**
	 * returns if valid sessions (typically users can create channels), if
	 * false only server can createChannels
	 * @return
	 */
	public boolean sessionsCanCreateChannels(){
		return true;
	}

	/**
	 * A few cases
	 * 	- the channel exists or doesn't
	 *  - sessions can create channels on the fly, or can't
	 * Question
	 *  - if the channels doesn't exist, we should probably save all messages to it
	 *  
	 * If sessions can create channels, this will create a new channel
	 */
	public void sendMessage(Message m){
		ChannelInterface n = null;
		ChannelInterface f = null;
		synchronized(channelMap){
			n = channelMap.get(m.to().toString());
			if(m.from() != null && m.from().hash() != null){
				//System.out.println("\nHAS FROM" + m.from().toString());
				f = channelMap.get(m.from().toString());
			}
				
		}
		//System.out.println("Domain "+m.to().domain()+" sending to "+m.to().toString()+(n != null? "" : "=no one!" ));
		if(n != null) n.sendMessage(m);
		if(f != null){
			f.resume();
		}
	}
	
	void addChannel(ChannelInterface n){
		//System.out.println("  DOMAIN:ADDING CHANNEL "+n.address().toString());
		synchronized(channelMap){
			channelMap.put(n.address().toString(), n);
		}
	}
	void removeChannel(ChannelInterface n){
		//System.out.println("Domain:removeChannel "+n.address().toString());
		synchronized(channelMap){
			channelMap.remove(n.address().toString());
		}
	}
	public ChannelInterface getChannel(Address a){
		String address = a.toString();
		ChannelInterface n = null;
		synchronized(channelMap){
			n = channelMap.get(address);
		}
		return n;
	}
	/*
	 * gets a channel, but can create it if it doesn't exist and the rules are right
	 */
	public Channel getOrCreateChannel(Address a) throws FilterException {
		//at some point check domain, it might be possible to create arbitrary channels.
		//however at this point, no one would be listening
		Channel n = (Channel) getChannel(a);
		if(n == null){
			if(ruleCreateByListen())
				n = new Channel(a);
			else
				throw new FilterException("{error: 'channel "+a.toString()+" does not exist'}");
		}
		return n;
	}
	public List getChannelList(Address a){
		String address = a.toString();
		List n = null;
		synchronized(channelListMap){
			n = channelListMap.get(address);
		}
		return n;
	}
	public List getOrCreateChannelList(Address a){
		String address = a.toString();
		List n = null;
		synchronized(channelListMap){
			n = channelListMap.get(address);
			if(n == null){
//				System.out.println(" Creating channel list");
				n = new List(a);
				channelListMap.put(address, n);
			}
		}
		return n;
	}
	
	
	
	public JSONObject map() throws JSONException{
		JSONObject json = new JSONObject();
		synchronized(channelMap){
			for(Iterator<Map.Entry <String, ChannelInterface>> iter = channelMap.entrySet().iterator(); iter.hasNext();){
				Map.Entry<String, ChannelInterface> entry = iter.next();
				ChannelInterface n = entry.getValue();
				json.put(n.address().toString(), n.map() );
				//we could remove, but it will come back very soon.
			}
		}
		return json;
	}
	
	//Rules (inherited by Database)
	public boolean ruleCreateByListen(){
		return true;
	}
	public String toString(){
		return address.toString();
	}
	
}
